<?php
/**
 * @package qBase
 * @subpackage base
 */

/**
 * The basic event object
 * 
 * @package qBase
 * @subpackage base
 */
class QEvent extends QObject 
{
	/**
	 * the delegates for this event
	 *
	 * @var QDelegate
	 */
	protected $delegates;
	/**
	 * The parameters that the sender sets to the event when it triggers it
	 * the sender may also set variables for complex types of events
	 *
	 * @var mixed
	 */
	public $parameters;
	/**
	 * The default parameters will merge with the specified parameters if some keys in the parameters are not set
	 *
	 * @var mixed
	 */
	public $default_parameters;
	/**
	 * The object that triggered the event
	 * only set on triggerEvent
	 *
	 * @var Object
	 */
	protected $sender;
	/**
	 * if this variable is set to false it stops the propagation of the event
	 * this variable is se to false on each trigger
	 *
	 * @var boolean
	 */
	protected $cancel;
	/**
	 * The type of the event
	 * 
	 * @var integer
	 */
	private $type;

	
	/**
	 * The constructor of QEvent
	 *
	 * @param mixed $type The type of the event
	 * @param array $delegates The list of delegates to link to the event (optional)
	 */
	public function __construct($type = null, $delegates = null)
	{
		parent::__construct();
		
		$this->type = $type;
		$this->delegates = $delegates;
	}
	
	/**
	 * Creates and adds a delegate to the event returns the created delegate
	 *
	 * @param object $callback_object
	 * @param string $callback_method
	 * @param mixed $parameters
	 * @param string $type the class of the delegate to be created, the default is QDelegate
	 * @return QDelegate 
	 */
	final public function addDelegate($callback_object, $callback_method, $parameters = null, $type = "QDelegate")
	{
		// VERY IMPORTANT ! SECURITY RELATED
		if (!QObject::IsA($type, "QDelegate"))
			throw new Exception("QEvent::addDelegate() type must be an object of QDelegate");
		eval("\$delegate = new $type(\$callback_object, \$callback_method, \$parameters);");
		if (!$this->delegates)
			$this->delegates = array();
		$this->delegates[] = $delegate;
		return $delegate;
	}
	
	/**
	 * Adds an existent delegate to the event
	 *
	 * @param QDelegate $delegate
	 */
	final public function addDelegateByRef(QDelegate $delegate)
	{
		if (!isset($this->delegates))
			$this->delegates = array();
		$this->delegates[] = $delegate;
	}
	
	/**
	 * Adds existent delegates to the event
	 *
	 * @param array $delegates
	 */
	final public function addDelegatesByRef($delegates)
	{
		if (!$this->delegates)
			$this->delegates = array();
		foreach ($delegates as $d)
			$this->delegates[] = $d;
	}
	
	/**
	 * Removes the specified delegate from the collection
	 * returns true if found, flase if not
	 *
	 * @param QDelegate $delegate
	 * @return boolean
	 */
	final public function removeDelegate(QDelegate $delegate)
	{
		if (!$this->delegates)
			return false;
		$pos = array_search($delegate, $this->delegates, true);
		if ($pos !== false)
		{
			array_splice($this->delegates, $pos, 1);
			return true;
		}
		else 
			return false;
	}
	
	final public function removeAllDelegates()
	{
		if (isset($this->delegates))
			unset($this->delegates);
	}
	
	/**
	 * Gets the number of delegates attached to this event
	 *
	 * @return integer
	 */
	final public function getDelegatesCount()
	{
		if (!$this->delegates)
			return 0;
		else 
			return count($this->delegates);
	}
	
	/**
	 * Gets the delegate at the specified position
	 * returns null if none found
	 *
	 * @param integer $pos
	 * @return QDelegate
	 */
	final public function getDelegateAt($pos)
	{
		if (!$this->delegates)
			return null;
		else if (($pos >= count($this->delegates)) || ($pos < 0))
			return null;
		else 
			return $this->delegates[$pos];
	}
	
	/**
	 * Triggers the event for the specified sender , with the specified 
	 * parameters (optional), for derivated events the code that triggers
	 * the event may also set variables specific to the event type
	 *
	 * @param QObject $sender
	 * @param mixed $parameters
	 */
	final public function triggerEvent(QObject $sender, $parameters = null)
	{
		$this->sender = $sender;
		
		$this->parameters = $parameters;
		if (isset($this->default_parameters))
		{
			if (!isset($this->parameters))
				$this->parameters = $this->default_parameters;
			else  if (is_array($this->default_parameters) && is_array($this->parameters))
			{
				foreach ($this->default_parameters as $k => $v)
					if (!isset($this->parameters[$k]))
						$this->parameters[$k] = $v;
			}
		}
		$this->cancel = false;
		if ($this->delegates)
			foreach ($this->delegates as $delegate)
			{
				if ($this->cancel)
					break;

				$delegate->execute($this);
			}
	}
	
	public function getMinimalParameters($new_params = null, $merge_params = false)
	{
		if ($merge_params && $new_params && (is_array($new_params)) && isset($this->parameters) && is_array($this->parameters))
		{
			$new_params = array_merge($this->parameters, $new_params);
			/*$this->debug($this->parameters);
			$this->debug($new_params);
			$this->debug($z);*/
		}
		
		if (!$new_params)
			$new_params = &$this->parameters;
			
		if (!$new_params)
			return null;
		else 
		{
			if (!$this->default_parameters)
				return $new_params;
			else 
			{
				// we have them both
				if (is_array($new_params) && is_array($this->default_parameters))
				{
					$minim = array();
					foreach ($new_params as $k => $v)
						if (!(is_string($k) && isset($this->default_parameters[$k]) && ($this->default_parameters[$k] == $v)))
							$minim[$k] = $v;
							
					if (count($minim) > 0)
						return $minim;
					else 
						return null;
				}
				else 
					return $this->parameters;
			}
		}
	}
	
	public function getParameters()
	{
		if (isset($this->parameters))
			return $this->parameters;
		else 
			return null;
	}
	
	public function getParameter($key)
	{
		if (isset($this->parameters) && isset($this->parameters[$key]))
			return $this->parameters[$key];
		else 
			return null;
	}
	
	public function setParameter($key, $value)
	{
		if (!isset($this->parameters))
			$this->parameters = array();
		$this->parameters[$key] = $value;
	}
	
	/**
	 * Sets the status of cancel for this event
	 * if cancel is true the propagation of the event is stoped
	 *
	 * @param boolean $value
	 */
	final public function setCancel($value)
	{
		$this->cancel = $value;
	}
	
	/**
	 * Cancels the propagation of the event
	 *
	 */
	final public function cancel()
	{
		$this->cancel = true;
	}
	
	public function getType()
	{
		return $this->type;
	}
	
	public function setDefaultParameters($params)
	{
		$this->default_parameters = $params;
	}
	
	public function getDefaultParameters()
	{
		return $this->default_parameters;
	}
}

?>